#ifndef __EPSILON_INPUT__KEYBOARD_H__
#define __EPSILON_INPUT__KEYBOARD_H__

#include <vector>

namespace Epsilon
{
class Keyboard_Listener;

class Keyboard {
public:
  Keyboard (void) { };

  typedef unsigned int KeyCode;

  enum KeyType {
    Text,
    Misc,
    Modifier,

    _NUM_KEY_TYPES
  } KeyTypes;


  class Key
  {
    public:
      Key (void)                       : code_ (0),    type_ (_NUM_KEY_TYPES) { }
      Key (KeyCode code, KeyType type) : code_ (code), type_ (type)           { }

      KeyType getType (void) const { return type_; }
      KeyCode getCode (void) const { return code_; }

    protected:
    private:
      KeyCode code_;
      KeyType type_;
  };

  class TextKey : public Key {
    public:
      TextKey (KeyCode code) : Key (code, Text) { }
  };

  class MiscKey : public Key {
    public:
      MiscKey (KeyCode code) : Key (code, Misc) { }
  };

  class ModifierKey : public Key {
    public:
      ModifierKey (KeyCode code) : Key (code, Modifier) { }
  };


  class Keys {
    friend class Keyboard;
    public:
      Keys (void) { };

      virtual bool         getKey     (KeyCode code) const = 0;
      virtual const char*  getKeyName (KeyCode code) const = 0;

    protected:
      virtual void         reset      (void)                     = 0; ///< Reset all key states
      virtual void         setKey     (KeyCode code, bool state) = 0;
  };

  class ModifierKeys : public Keys {
    friend class Keyboard;
  public:
    enum Mask
    {
      Command  = 0x100,    // Bit: 8
      Shift    = 0x200,    // Bit: 9
      CapsLock = 0x400,    // Bit: 10
      Alt      = 0x800,    // Bit: 11
      Control  = 0x1000,   // Bit: 12
//             0x2000,
//       0x4000,
//       0x8000,
      NumLock  = 0x10000,  // Bit: 16
      Fn       = 0x20000,  // Bit: 17
    };

           ModifierKeys (void);
  virtual ~ModifierKeys (void) { };


    bool        getKey     (KeyCode code) const;
    const char* getKeyName (KeyCode code) const;

  protected:
    void        reset      (void);
    void        setKey     (KeyCode code, bool state);

  private:
    int mask_;
  };

  class TextKeys : public Keys {
    friend class Keyboard;
  public:
    enum Keys {
      A, B, C, D, E, F, G, H, I, J, K, L, M,
      N, O, P, Q, R, S, T, U, V, W, X, Y, Z,

      Zero, One, Two,   Three, Four,
      Five, Six, Seven, Eight, Nine,

      Exclamation,      // !
      At,               // @
      Pound,            // #
      Dollar,           // $
      Percent,          // %
      Carrot,           // ^
      Ampersand,        // &
      Asterisk,         // *
      LeftParenthesis,  // (
      RightParenthesis, // )

      Minus,            // -
      Equals,           // =

      Underline,        // _
      Plus,             // +

      Tilde,            // ~
      WHATTHEHELL,      /// `


      LeftBracket,
      RightBracket,
      BackSlash,

      LeftCurlyBracket,
      RightCurlyBracket,
      Pipe,

      SemiColon,
      Quote,
      Comma,
      Period,
      Slash,

      Colon,
      DoubleQuote,
      LeftAngleBracket,
      RightAngleBracket,
      Question,

      _NUMBER_OF_KEYS
    };

             TextKeys (void);
    virtual ~TextKeys (void) { };

    bool        getKey     (KeyCode code) const;
    const char* getKeyName (KeyCode code) const;

  protected:
    void        reset      (void);
    void        setKey     (KeyCode code, bool state);

  private:
    bool keys_ [_NUMBER_OF_KEYS]; // Current state for all keys
  };

  class MiscKeys : public Keys {
    friend class Keyboard;
  public:
    enum Keys
    {
      Insert,
      Delete,
      Home,
      End,
      PageUp,
      PageDown,
      Tab,
      Up,
      Down,
      Left,
      Right,
      Enter,
      Space,
      Backspace,
      Escape,
      F1,
      F2,
      F3,
      F4,
      F5,
      F6,
      F7,
      F8,
      F9,
      F10,
      F11,
      F12,

      Print,
      ScrollLock,
      Pause,

      NumPad_Equals,
      NumPad_Period,
      NumPad_Multiply,
      NumPad_Minus,
      NumPad_Plus,
      NumPad_Divide,
      NumPad_0,
      NumPad_1,
      NumPad_2,
      NumPad_3,
      NumPad_4,
      NumPad_5,
      NumPad_6,
      NumPad_7,
      NumPad_8,
      NumPad_9,
      NumPad_Enter,

//      Power,

      _NUMBER_OF_KEYS
    };

             MiscKeys (void);
    virtual ~MiscKeys (void) { };

    bool        getKey     (KeyCode code) const;
    const char* getKeyName (KeyCode code) const;

  protected:
    void reset             (void);
    void setKey            (KeyCode code, bool state);

  private:
    bool keys_ [_NUMBER_OF_KEYS];
  };


  const char* getKeyName     (Key key) const;
  const char* getKeyTypeName (Key key) const;

  /* Return a character representing the passed key, if one exists. */
  static const char getKeyChar (       Key           key,
                                 const ModifierKeys* pModifiers = NULL );

  const ModifierKeys* getModifierKeys (void) const;


  bool GetKey     (Key key) const;

  bool InstallListener   (Keyboard_Listener* pListener);
  bool UninstallListener (Keyboard_Listener* pListener);

///protected:
  void PressKey   (Key key);
  void ReleaseKey (Key key);

  void ResetKeys  (void);     ///< Call this when all keyboard states need to
                              ///   be reset...

private:
  void SetKey     (Key key, bool state);

  TextKeys     text_;
  MiscKeys     misc_;
  ModifierKeys modifier_;

  std::vector <Keyboard_Listener *> listeners_;
};

/*
enum ModifierKeyCodes {
  Record       = 0x36,
  Command      = 0x37, // Windows Key
  LeftShift    = 0x38,
  CapsLock     = 0x39,
  LeftOption   = 0x3A, // Alt (Left)
  LeftControl  = 0x3B,
  RightShift   = 0x3C,
  RightOption  = 0x3D, // Alt (Right)
  RightControl = 0X3E,
  Fn           = 0x3F,
};

const int numLockKeyBit = 16;
const int fnKeyBit      = 17;
*/

class Keyboard_Listener {
public:
           Keyboard_Listener (void) { };
  virtual ~Keyboard_Listener (void) { };

  virtual bool OnKeyPress   (Keyboard::Key& key) = 0;
  virtual bool OnKeyRelease (Keyboard::Key& key) = 0;

protected:
private:
};

}

#endif /* __EPSILON_INPUT__KEYBOARD_H__ */
